多重起動1−2(FindWindow_2)
多重起動1 の続きとなるが見てないやつは先に見ておけ。
前回は FindWindow やスタックに関する基本的な動作について教えたが今回からは プログラム の解析に入る。
まずは Ollydbg で読み込み、Call FindWindow まで実行させてみろ。
もう引数やスタックの確認はだいじょうぶだな?
0040100A |. E8 F11F0000 CALL ; \FindWindowA
次に一度だけ F8 を押し、ウィンドウを確認すると cmp 命令を行おうとしているのが分かるはずだ。
0040100F |. 3D 00000000 CMP EAX,0
cmp とは比較命令だ、比較の対象とするのは EAX と 0(ゼロ)
cmp 命令で比較した結果、両方の値が同じ(真)場合は ZFフラグを 1 にする。
違う値(偽)であれば ZFフラグを 0(ゼロ)にする。
では早速解析といこうか。
まず、cmp 命令についてだが eax と 0(ゼロ)を比較している。 ここで eax を比較の対象に指定しているがなぜだろうか?
以前にも伝えたが API や関数の戻り値は基本的に eax に返されると説明したのは覚えているだろう。
では FindWindow を実行した後に返される値、eax に反映される値はなんであろうか?
MSDN で FindWIndow の説明を確認してみると以下の一文がある。
-- MSDN より抜粋 --
戻り値
関数が成功すると、指定したクラスとウィンドウ名を持つウィンドウのハンドルが返ります。
関数が失敗すると、NULL が返ります。拡張エラー情報を取得するには、
GetLastError 関数を使います。
成功した場合はウィンドウのハンドルの値が返されるが、失敗すると NULL(0) が返されるらしい。
なるほど、ということは FindWindow を実行した結果として、eax には成功の判別である戻り値(0 or ハンドルの値)が格納されることになる。
つまり、
FindWindow の引数として指定した文字列がデスクトップ上のウィンドウのタイトルに含まれていない場合は
eax にゼロを渡すということだ。
これを先ほどの cmp 命令に当てはめると、FindWindow が失敗した時の値(0)と等しいか?の比較をしていることがわかる。
これらを頭に入れ、Ollydbg のデバッグ画面を見てみると以下のように考えることが出来るだろう。
まず FindWindow を実行する。
0040100A |. E8 F11F0000 CALL ; \FindWindowA
FindWindow を実行した場合、その戻り値は eax に格納される。
次に cmp 命令で eax と ゼロが均しいかを比較する。
指定したタイトルを持つウィンドウがある場合は、eax にハンドルの値が入る。
そうでなければ eax にはゼロが入る。
0040100F |. 3D 00000000 CMP EAX,0
eax がゼロの場合は ZFフラグが 1となり、eax にハンドルの値が入っている場合は
ZFフラグがゼロとなる。これにより次の jnz 命令が生かされてくる。
00401014 |. 75 1A JNZ SHORT fw_basic.00401030
jnz 命令はZFフラグがゼロの場合に指定したアドレスへ処理を飛ばす。
今回でいえば、ZFフラグが 1 の場合は「あぼーん」の MessageBox へ。
ZFフラグが 0(ゼロ)の場合は「既に起動しています」の処理へジャンプする。
それでは多重起動を回避するアプローチを考えようか。
まず考えられるのがジャンプ先を全て「あぼーん」の MessageBox に飛ばす方法、次に cmp 命令の比較結果がどんな時でも
真、つまりZFフラグが1にならないようにする方法だ。
まずは前者からやっていこう。
このプログラム内で唯一ジャンプする命令があるのは JNZ 命令のみである。
よってこれを変えるわけだが、JNZ 命令の飛び先となるアドレスを「あぼーん」のMessageBox が始まるアドレスに書き直すだけだ。
00401014 |. 75 1A JNZ SHORT fw_basic.00401016
次にもう一つの手段として cmp 命令の比較結果を真にする方法だが、同じ値同士を比較させれば真になるというのは
安易に想像がつくだろう。
ではさっさと書き換えてみようか。
0040100F 3BC0 CMP EAX, EAX
00401011 90 NOP
00401012 90 NOP
00401013 90 NOP
書き換えた後で気づいたと思うが、NOP という文字列が入っている。この NOP とは何もしない命令文だ。
よく使われるケースとしては、
プログラムを書き換えた後に無駄なスペースが出来てしまった場合、
そのスペースを埋めるために使われるケースや、何らかの処理自体を無効にするため、埋め立てるために使われる事が多い。
ちなみに Ollydbg の場合は自動で挿入されるようになっている。必要がなければ「オプション」の設定で好きにしてくれ。
以上で FindWindow による多重起動の授業は終了となるが、自分でやってみて決して難しくないことが分かったはずだ。
それでは最後に簡単なメモを残しておこう。
・API(関数)を使う場合、必要な引数はスタックに push する。
・一般的に API(関数)を実行し、戻される値(戻り値)は eax に格納される。
・NOP とは、何もしない命令。スペースを埋めたり処理自体をつぶすために使われる。
さて、次回の授業は 多重起動2(CreateMutex) だ。
これも基本中の基本だからしっかり勉強していけ。
|